WxBeacon2 を使った室内環境監視
Motivation
kinoppyd さんがやっていたのを見たら面白そうだったのと、ちょうどスマートホーム周りをいじっていたので。
TL;DR
室内環境のモニタリングをしたい
WxBeacon2 + Raspberry Pi + AWS IoT + AWS CloudWatch Metrics (+ S3)
https://gyazo.com/758341c376ef66777568ede5573afe1c
最終的にできた画面。
https://gyazo.com/ba79b25bcdf976db24ca960be28eebfa
料金について
AWS IoT
1 年繋ぎっぱなしでだいたい 0.06 USD
30 秒毎にメッセージを送信だと、年間 (2 * 60 (m) * 24 (h) * 365 (day)) = 1051200 回で、だいたい 1.6 USD くらい
ルールエンジンとアクションが1つずつだと、どちらも 1051200 回 で、だいたい 0.4 USD くらい
0.06 + 1.6 + 0.4 = 2.06 USD くらいなので、大した料金ではない
1. BLE のおさらい
あまり詳しくないので、さらっとおさらいしておく。
Central/Peripheral
Central がクライアント側、Peripheral がサーバ側で、以下のような手順で通信を行う。
1. Advertise
Peripheral が、周囲に電波を発する
2. Scan
Central が、周辺の Peripheral を探す
3. Connect
Central が、特定の Peripheral と接続を開始する
4. Disconnect
接続を解除する
2. WxBeacon2 について
とは
中身は以下の 2JCIE-BL01 らしい。
情報
BLE のインターフェース仕様。公式かどうかは不明。
どうやら公式から配布されている User's Manual があるようで、その内容が上記ページにあるようだ。
通信用のサンプルコード。こちらも公式かどうかは不明。
参考サイト
3. 2JCIE-BL01 仕様を読んでみよう
時間があるしさらっと目を通してみる。
With/Without Recording Mode
2JCIE-BL01 には、複数の Beacon mode があり、これによって送信される情報や、動作の仕方が変化する。
この Beacon mode は全部で 8 つあるが、それらのうちの半分は、データの記録を行う With Data Recording Mode、もう半分は出たの記録をメモリ上に行う Without Data Recording Mode となっており、デフォルトでは後者になっている。両者の具体的な違いは、Latest Data を更新した後に、それをメモリ上に記録するか?しないか?という点と、Latest Data を Advertise にのせるか?のせないか?という点となる (詳しくは仕様書のフローチャートを参照)。
1.3. Operation flow
According to set Beacon Mode, there are two operation patterns with and without measured data recording. The sensor data measurement and recording to flash memory are carried out regardless of whether they are connected or disconnected to/from the Central device. Further details of Beacon Mode are described in 3. Advertise format.
Without Data Recording Mode
Advertise の電波にセンサーデータを乗せて、常に送る。
With Data Recording Mode
Advertise にはセンサーデータを乗せず、Central から Connect された後に、センサーデータを取得させる。メモリー上にデータを保持する。
メモリ上に保持する場合の保持の仕方
今回は使わないが、軽く見ておく。
Connect した Central は、まず最初に時間に関する設定 (Time Information) を設定し、その後センサデータの記録が開始される。この Time Informtion は、以下の 3 つのケースで 0 にリセットされる。
計測間隔が変更された
Beacon Mode が変更された
バッテリーが交換、もしくは再起動した
上記に該当するケースが生じた場合は、その度に Time Information を再設定しなければ、記録は開始されない。
メモリ上にデータを保持する場合、データは Page という単位で扱われ、0 ~ 2047 の 2048 ページまで記録される。1 つのページは UNIX TIME と 13 の計測データを保持する行から構成される。1 行目に取得開始時間の UNIX TIME が保持され、その次の行に取得時間間隔、そして次の行からはセンサーデータが入る。センサーデータは時間間隔毎に保持される。センサーデータ行が Row 0 - 12 の 13 行分保持されると、次のページに移動する。
table:1ページ分の情報
格納情報 内容 例
計測開始時間 0x5685C180 (1451606400) 2016/1/1 00:00:00
計測間隔 0x12C (300 sec) 5 分毎
Row 0 センサーデータ 2016/1/1 00:00:00 時のセンサーデータ
Row 1 センサーデータ 2016/1/1 00:05:00 時のセンサーデータ
... ... ...
Row 12 センサーデータ 2016/1/1 01:00:00 時のセンサーデータ
GATT サービス
GATT には Service という概念があり、その一覧は 2 章にのっている。GATT Service には対応する UUID が割り振られるが、仕様で定められている GATT Service (Public Service) は 16 byte の省略形が利用でき、カスタムな GATT Service の場合は UUID を全て指定しなければならないという仕様になっている。カスタムサービスは、下記の UUID 0C4CXXXX-7700-46F4-AA96D5E974E32A54 をベースとして、XXXX 内の値を対応する値に置き換えたものを利用する。 table:GATT Service
UUID サービス名 概要
0x3000 センサーサービス Latest Data やページ、その他内部状態等のデータを取得する
0x3010 設定サービス 各種センサーの設定を読み書きする
0x3030 制御サービス デバイスの制御値 (Time information, LED 等) の読み書きをする
0x3040 パラメーターサービス Bluetooth 通信の設定値 (Advertise mode, uuid 等)の読み書きをする
0x3050 DFU サービス BLE 通信経由でのファームウェアアップデート
0x1800 GAS (Public Service) 略
0x1801 GATT (Public Serbice) 略
0x130A デバイス情報サービス (Public Service) 略
細かい仕様は、2 章に各々対応する節があるので、読めば良さそう。そして、これらサービスの内容を確認 & 編集するのに有用なスマホアプリが BLE Scanner である。
これを使えば、各種サービスの設定状況の確認と、その設定の変更が行える。後ほど、モードの切り替えを行う場合もこのアプリを利用する。
Beacon mode
Beacon mode によって変更するのは、以下の要素となる。
Data Recording
データをメモリ上に記録するか、Advertise パケットに乗せて送信するか
Beacon mode との対応関係は、仕様書の Table 2, 3 にある
Advertise format
Advertise パケットの形式
Beacon mode との対応関係は、仕様書の Table 33、具体的なフォーマットは 3 章にある
Advertise format については、同じ Without data format に分類される Beacon mode でも、送信されるセンサ情報の内容が多少変わっている場合がある。Advertise format の一覧は以下の通り。
(A) Beacon
iBeacon と同様のフォーマット
(B) Connection Advertie 1
最新のセンサー値、最新のページの情報、イベントフラグを保持する
(C) Connection Advertise 2
最新のページ情報、イベントフラグは保持するが、センサーデータは保持しない
(D) Sensor ADV 1
最新のセンサーデータとイベントフラグを保持する
加速度も保持する
(E) Sensor ADV 2
イベントフラグをセンサーデータを保持する
加速度を保持しない代わりに、不快度指数や熱中症危険度等を保持する
table:beacon mode
値 名前 略称 デバイス名 with/without advertise format advetise format (検知時)
0x00 Event Beacon (SCAN RSP) Env EnvSensor-BL01 with (B) (A)/(B)
0x01 Standard Beacon Env EnvSensor-BL01 with (B)
0x02 General Broadcaster 1 IM IM-BL01 without (D)
0x03 Limited Broadcaster 1 IM IM-BL01 without (D)
0x04 General Broadcaster 2 EP EP-BL01 without (E)
0x05 Limited Broadcaster 2 EP EP-BL01 without (E)
0x07 Alternate Beacon Env EnvSensor-BL01 with (A)/(B)
0x08 Event Beacon (ADV) Env EnvSensor-BL01 with (C) (A)/(C)
4. やっていき
4.0 なにをすべきか?
目標を達成するためにすべきことは以下。
1. WxBeacon2 の Beacon mode を決定し、変更する
2. Raspberry Pi から WxBeacon2 のセンサ値を取得する
3. 取得したセンサ値を、Raspberry Pi から AWS IoT に送信する
4. AWS IoT から、S3, CloudWatch Metrics にデータを送信する
順番に見ていく。
4.1. WxBeacon2 の Beacon mode を決定し、変更する
どの Beacon mode にすべきか?が決まれば、どうデータを取得するかも決まるはず。参考サイトは Without Data Recording Mode を採用していた。With Data Recording Mode だと、コネクションのたびに取得間隔を設定する必要があるし、そもそもまずコネクションをはる必要がある。持ち歩く場合はコネクションを張っていた方が良いが、自宅に置いておくならコネクションをはる必要はないし、扱いやすい、ということかな。自分もそれで良いと思う。
ただ、Without Data Recording Mode にあたる Beacon mode は全部で 4 つある。ここからさらに選択する必要がある。主に以下の2つの軸で違いがある。
IM or EP
これらが違うと、Advertise format が異なる
IM が Sensor ADV1, EP が Sensor ADV2
主な違いは、加速度センサを使うか、その他のセンサ (不快度指数や熱中症危険度) を使うか、である
どちらも使うつもりはないのだけど、加速度センサは特にいらない (持ち運ばないから) ので、EP を選択する
General or Limited
これらの違いは仕様書に詳しくは書かれていないが、Limited Broadcaster のみ、Transmission period, Silent period が設定できる
すなわち、Advertise パケットを送信する頻度や、送信しない頻度を設定できる
電池消費量を抑えるだとか、そういう目的では使えそうだが、設定項目が増えて面倒なので、今回はとりあえず General Broadcaster にする
EP かつ General Broadcaster なのは、General Broadcaster 2 になるので、0x04 を設定してく (結局、参考サイトと同じ選択になった)。
Advertise format を変更する Service はカスタムサービスの内の パラメーターサービス (0x3040) になりそうなので、ここをいじる。パラメーターサービスについてはマニュアルの 2.4.1 に説明がある。この Service の Characteristic は以下の2つで、いじるのは後者 (2.4.2)。
table:characteristic
UUID Characteristic 内容
0x3041 UUID UUID, Major, Minor
0x3042 ADV 設定 Advertise 設定
BLE Scanner の使い方は参考サイトが詳しいので自筆すべきことはないのだけど、
初期状態だと、WxBeacon2 の BLE Scanner 上での表示名は Env になっている
Connect すると、WxBeacon2 の LED が点滅する
みたいなことがあった。
Beacon mode はデフォルトだと 08 になっていたので、これを 04 に変更し、再接続を行う。と、名前が Env から EP-BL01 に変更して表示されるようになった。ちなみに、変更前と後の 0x3042 サービスの値は以下。
デフォルト値: 0x0808 a0000 0a00 3200 08 00
変更後の値: 0x0808 a0000 0a00 3200 04 00
4.2. Raspberry Pi から WxBeacon2 のセンサ値を取得する
下記サンプルコードを利用する。
動作環境は以下。
Raspberry Pi 3 Model B
Raspbian Jessie with Pixel
Python は適宜導入。
code:shell
$ python --version
Python 2.7.9
# 必要パッケージのインストール
$ sudo apt-get install python-bluez
# 実行
$ cd envsensor-observer-py/envsensor-observer-py
$ chmod +x envsensor_observer.py
$ sudo ./envsensor_observer.py
これで、デフォルトだと log/env_sensor_log.csv 以下に CSV ファイルが結果としてはかれる。conf.py が設定ファイルになっているので、そちらを設定すれば fluentd や influxdb と連携させることができる。
Broadcast mode になっていれば、ログが正常に出力されているはず。
4.3. 取得したセンサ値を、Raspberry Pi から AWS IoT に送信する
AWS IoT では、ある MQTT メッセージに対して ルール と アクション を定義できる。アクション は他の AWS リソースと連携する何らかの動作で、ルール はそれをまとめたものになる。
今回作りたい アクション は以下なので、これらを ルール にまとめる。
受け取ったセンサー値を、Cloudwatch Metrics に Put する
センサー値を、S3 にも出力する
CloudWatch のデータは 2 習慣で消えてしまうため
S3 に置いておけば後から何らかの形で利用できそうなので、とりあえず
なにかエラーがあった場合は、S3 にエラーメッセージを出力する
また、送信されてきたデータをアクションを介して他の AWS リソースに渡す場合、何らかの方法でデータを抽出、整形する必要がある。例えば、今回の場合だとセンサー値を一度に全て publish するが、CloudWatch Metrics に渡したいのはそのうちの一部になる。このような場合は、SQL クエリ を利用してデータの抽出を行う。そして、これを行いやすいように、AWS IoT に送信するデータの形式は JSON フォーマットになっていることが望ましい。
ので、まずは envsensor-observer-py を JSON フォーマットに対応するように拡張した。下記の、feature/for-aws-iot ブランチに push している。
この拡張したプログラムを行う前に、AWS Console の IoT Core コンソール上から、以下を済ませておく必要がある。
管理 > モノ から モノ の作成
単一のモノを作成する を押下する
タグ等は特に設定せず次へ
1-Click 証明書作成 の 証明書の作成 を押下する
ここで発行された3つの証明書はローカルに DL しておく
完了
安全性 > ポリシー から ポリシー の作成
適切なポリシーをつける
終わったらモノにアタッチする
上記の作成が終わったら、拡張した envsensor-observer-py を Raspberry Pi 上に clone しなおし、conf.py の AWS_IOT の項目を全て埋める。クライアント ID はモノの名前 (Thing name) と同一。CERTIFICATE_PATH 等の証明書のパスは、先ほど DL した証明書を Raspberry Pi に送信し配置したパスを書き込む。ENDPOINT はモノの 操作 ページから確認できる。トピック名は好きな名前で問題ない。今回は wxbeacon2/values あたりにした。
これで動くはずなので、テストを行う。Raspberry Pi 側では、sudo ./envsensor_observer.py でプログラムを動かす。
AWS コンソール側では、テスト ページへ行き、トピックのサブスクリプション の欄に設定したトピック名 (今回は wxbeacon2/values) を入力し、トピックへのサブスクライプ を押下する。
デフォルトのままだと 5 分おきにデータが出力されるので、出力されるのを待つ。確認できれば OK。
4.4. AWS IoT から、S3, CloudWatch Metrics にデータを送信する
最後に、AWS IoT から S3, CloudWatch Metrics に送信するために、ルールとアクションの設定を行う。まず、ルールクエリステートメントは以下のようにする。トピック名が wxbeacon2/values なので、そこにきたデータを全て取得する。
code:sql
SELECT * from 'wxbeacon2/values'
そして、アクションの設定については、CloudWatch Logs は以下のような感じ。値は 置換テンプレート を利用して ${humidity} のように書く。JSON の値が入れ子になっている場合どう取り出せば良いのかわからなかったので、今回は全て JSON 内にフラットに値を格納している。ので、単純に値の名前を ${} 内に記述すれば取得できる。 また、単位について、指定できる値はここにあるようだ。今回は全て None にしてある。
https://gyazo.com/eb8ef189a6147c13fd48229af489de33
IAM ロールには下記のポリシーをアタッチした。
AWSIoTThingsRegistration
AWSIoTLogging
AWSIoTRuleActions
メトリクスは wxbeacon2 で統一し、メトリクス名は対応するものを適切につけた。
また、S3 の設定は以下のような感じ。キーには被らないようにタイムスタンプを利用する。
https://gyazo.com/57317eaa6da6cbd7953df16593d36682
動作確認
きちんと CloudWatch Metrics に値が送信されていると、以下のようにメトリクスが追加される。
https://gyazo.com/54e605a063e395bef620efc2059f5549
また、CloudWatch 上ではダッシュボードが作成できる (3 つまでなら無料)。これを作成したら以下のような感じで室内環境をインターネット (AWS コンソール) 経由で確認できる。下記は、夜にこの仕組みができて、一晩放置してみた結果。明るさが顕著で、いつ寝ていつ起きたかがわかる。また、気温も、いつエアコンをつけたかなどがわかる。
https://gyazo.com/ba79b25bcdf976db24ca960be28eebfa
その他: Athena
後で何かに使えるようにとりあえず S3 にためている。JSON をそのままタイムスタンプ名でログファイルとして格納しているが、これは、Athena で以下のようなテーブルを作成してクエリを投げつけると取得できて凄い。いつか何かに使えるかもしれないので、メモしておく。
code:sql
CREATE EXTERNAL TABLE IF NOT EXISTS default.WxBEacon2 (
measurement string,
gateway string,
sensor_type string,
bt_address string,
temperature string,
humidity string,
light string,
uv string,
pressure string,
noise string,
di string,
heat string,
battery string,
timestamp string,
distance string
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://wxbeacon2/';
select * from wxbeacon2;
追記
日本語マニュアルが後から見つかって草だった